﻿// (c) Copyright Microsoft Corporation.
// This source is subject to [###LICENSE_NAME###].
// Please see [###LICENSE_LINK###] for details.
// All other rights reserved.

using System;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace EnsureWeb.Controls
{
    /// <summary>
    /// Time View is the base class of different scale of view.
    /// </summary>
    public abstract class TimeView
    {
        /// <summary>
        /// Gets the begin date of the view.
        /// </summary>
        public abstract DateTime BeginDate { get; }

        /// <summary>
        /// Gets the end date of the view.
        /// </summary>
        public abstract DateTime EndDate { get; }

        /// <summary>
        /// Gets or sets the current date of the view.
        /// </summary>
        public DateTime CurrentDate { get; set; }

        /// <summary>
        /// Gets or sets the begin date of parent scale view.
        /// </summary>
        public DateTime ParentBeginDate { get; set; }

        /// <summary>
        /// The sub items.
        /// </summary>
        private Collection<TimelineItemData> items;

        /// <summary>
        /// Gets the DateTime list for this time line view.
        /// </summary>
        public Collection<TimelineItemData> Items
        {
            get
            {
                if (items == null)
                {
                    UpdateTimelineItemData();

                }

                return items;
            }
        }

        /// <summary>
        /// The timeline control.
        /// </summary>
        private MultiScaleTimeline owner;

        /// <summary>
        /// Initialize a new instance of the TimeView class.
        /// </summary>
        /// <param name="timeline">Parent Timeline.</param>
        /// <param name="scale">The view scale.</param>
        protected TimeView(MultiScaleTimeline owner, TimelineScale scale)
        {
            this.owner = owner;
            this.TimelineScale = scale;
        }

        /// <summary>
        /// Create a timeline item.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <returns>A new instance of timeline item.</returns>
        public TimelineItem PrepareTimelineItem(int index)
        {
            TimelineItem item = new TimelineItem();
            item.Style = owner.TimelineItemStyle;
            item.Text = GetTimelineItemText(index);
            item.CanDrilldown = TimelineItemCanDrilldown(index);
            item.IsSelected = GetTimelineItemIsSelect(index);
            item.DateTime = GetTimelineItemDate(index);
            item.CanSelect = IsItemSelectable(index);
            return item;
        }

        /// <summary>
        /// Update current date.
        /// </summary>
        /// <param name="dateTime">New date value.</param>
        public void UpdateDate(DateTime dateTime)
        {
            CurrentDate = dateTime;

            ParentBeginDate = RollupDate;
        }

        /// <summary>
        /// Gets or sets the time scale of the view.
        /// </summary>
        public TimelineScale TimelineScale { get; set; }

        /// <summary>
        /// Gets the FirstYearOfDecade property of MultiScaleTimeline.
        /// </summary>
        protected YearOfDecade FirstYearOfDecade
        {
            get
            {
                return this.owner.FirstYearOfDecade;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the time scale can rollup.
        /// </summary>
        public bool CanRollup
        {
            get
            {
                return this.TimelineScale != owner.MaximumScale;
            }
        }

        /// <summary>
        /// Gets or sets the selected date.
        /// </summary>
        protected DateTime SelectedDate
        {
            get { return owner.SelectedDate; }
            set { owner.SelectedDate = value; }
        }

        /// <summary>
        /// Gets the number of sub items.
        /// </summary>
        protected abstract int ItemsCount
        {
            get;
        }

        /// <summary>
        /// Update the date of next page.
        /// </summary>
        public abstract void NextPage();

        /// <summary>
        /// Update the date of previous page.
        /// </summary>
        public abstract void PreviousPage();

        /// <summary>
        /// Go to next page.
        /// </summary>
        public void GotoNextPage()
        {
            NextPage();
            UpdateTimelineItemData();
        }

        /// <summary>
        /// Go to previous page.
        /// </summary>
        public void GotoPreviousPage()
        {
            PreviousPage();
            UpdateTimelineItemData();
        }

        /// <summary>
        /// update timelineitem date
        /// </summary>
        public void UpdateTimelineItemData()
        {
            items = new Collection<TimelineItemData>();

            for (int i = 0; i < ItemsCount; i++)
            {
                items.Add(new TimelineItemData()
                {
                    DateTime = GetTimelineItemDate(i),
                    Index = i,
                    Text = GetTimelineItemText(i),
                });
            }
        }

        /// <summary>
        /// Gets the next selectable date.
        /// </summary>
        public abstract DateTime NextSelectableDate { get; }

        /// <summary>
        /// Gets the timeline item text by index.
        /// </summary>
        /// <param name="index">Item index.</param>
        /// <returns>The timeline item text.</returns>
        protected abstract string GetTimelineItemText(int index);

        /// <summary>
        /// Gets the date time according the index.
        /// </summary>
        /// <param name="index">Item index.</param>
        /// <returns>Date time value of the item.</returns>
        protected abstract DateTime GetTimelineItemDate(int index);

        /// <summary>
        /// Gets a value indicating whether the date is selected.
        /// </summary>
        /// <param name="dateTime">The checked date.</param>
        /// <returns>True if the date is selected.</returns>
        public abstract bool IsDateSelected(DateTime dateTime);

        /// <summary>
        /// Check if a item is selectable.
        /// </summary>
        /// <param name="index">The item index.</param>
        /// <returns>true if the item is selectable.</returns>
        public abstract bool IsItemSelectable(int index);

        /// <summary>
        /// Check if a item can drill down to detail scale view.
        /// </summary>
        /// <param name="index">The item index.</param>
        /// <returns>True if a item can drill down to detail scale view.</returns>
        public bool TimelineItemCanDrilldown(int index)
        {
            bool canSel = IsItemSelectable(index);
            bool canDrillDown = this.TimelineScale != owner.MinimumScale;
            return canSel && canDrillDown;
        }

        /// <summary>
        /// Check if the specific item is selected.
        /// </summary>
        /// <param name="index">The item index.</param>
        /// <returns>True if the item is selected.</returns>
        public bool GetTimelineItemIsSelect(int index)
        {
            DateTime itemDate = GetTimelineItemDate(index);
            bool isSel = IsDateSelected(itemDate);
            return isSel;
        }

        /// <summary>
        /// Gets the rollup text.
        /// </summary>
        public abstract string RollupText
        {
            get;
        }

        /// <summary>
        /// Gets the rollup date.
        /// </summary>
        protected abstract DateTime RollupDate
        {
            get;
        }

        /// <summary>
        /// Get the timeline mark position.
        /// </summary>
        /// <param name="date">The timeline mark date.</param>
        /// <param name="timelineWidth">The timeline width.</param>
        /// <returns>Returns the timeline mark position.</returns>
        public virtual double GetTimelineMarkPosition(DateTime itemDate, double timelineWidth)
        {
            if (IsDateOutOfDisplay(itemDate) != 0)
            {
                throw new ArgumentOutOfRangeException("itemDate", "Invalid input");
            }

            double markPos;
            DateTime start = this.BeginDate;
            DateTime end = this.EndDate;
            TimeSpan total = end - start;
            TimeSpan markLen = itemDate - start;
            double total_days = total.TotalDays;
            double markLen_days = markLen.TotalDays;
            markPos = markLen_days / total_days * timelineWidth;
            return markPos;
        }

        /// <summary>
        /// Verify the date is selectable.
        /// </summary>
        /// <param name="date">The date value.</param>
        /// <returns>true if the input date is selectable.</returns>
        public bool IsDateSelectable(DateTime date)
        {
            if (owner.IsSelectableDateRangeValide == false)
            {
                return false;
            }
            if (owner.SelectableDateStart != null && date < owner.SelectableDateStart)
            {
                return false;
            }

            if (owner.SelectableDateEnd != null && date > owner.SelectableDateEnd)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Gets a value indicating whether the selected date is out of display.
        /// </summary>
        public int IsSelectedDateOutOfDisplay
        {
            get
            {
                int isOut = IsDateOutOfDisplay(SelectedDate);
                return isOut;
            }
        }

        /// <summary>
        /// Check if the date is out of the display.
        /// </summary>
        /// <param name="date">Checked date.</param>
        /// <returns>true if the input date is out of display.</returns>
        public int IsDateOutOfDisplay(DateTime date)
        {
            if (date < BeginDate)
            {
                return -1;
            }
            else if (date > EndDate)
            {
                return 1;
            }

            return 0;
        }

        /// <summary>
        /// Gets a value indicating whether item can go previous.
        /// </summary>
        public bool CanShowPrevious
        {
            get
            {
                if (owner.SelectableDateStart == null)
                {
                    return true;
                }

                bool canShow = owner.SelectableDateStart < this.BeginDate;
                return canShow;
            }
        }

        /// <summary>
        /// Gets a value indicating whether item can go next.
        /// </summary>
        public bool CanShowNext
        {
            get
            {
                if (owner.SelectableDateEnd == null)
                {
                    return true;
                }

                bool canShow = owner.SelectableDateEnd > EndDate;
                return canShow;
            }
        }

        /// <summary>
        /// Go to next item.
        /// </summary>
        /// <returns>True if succeeded.</returns>
        public bool Next()
        {
            DateTime nextSelectDate = this.NextSelectableDate;
            bool canSet = IsDateSelectable(nextSelectDate);

            if (canSet)
            {
                SelectedDate = nextSelectDate;
            }

            return canSet;
        }

        /// <summary>
        /// Check if a range of datetime is selectable.
        /// </summary>
        /// <param name="dateStart">Start of date.</param>
        /// <param name="dateEnd">End of date.</param>
        /// <returns>true if a range of datetime is selectable.</returns>
        public bool IsDateRangeSelectable(DateTime dateStart, DateTime dateEnd)
        {
            Debug.Assert(dateEnd >= dateStart, "Invalid input");

            if (owner.IsSelectableDateRangeValide == false)
            {
                return false;
            }

            if (owner.SelectableDateStart == null && owner.SelectableDateEnd == null)
            {
                return true;
            }

            if (owner.SelectableDateStart != null && owner.SelectableDateEnd == null)
            {
                return dateEnd >= owner.SelectableDateStart;
            }

            if (owner.SelectableDateStart == null && owner.SelectableDateEnd != null)
            {
                return dateStart <= owner.SelectableDateEnd;
            }

            if (IsDateSelectable(dateStart) || IsDateSelectable(dateEnd))
            {
                //The given Date Range is within the Selectable Date Range
                return true;
            }

            //If the given DateRange is overlapped with the Selectable Date Range
            return (dateStart < owner.SelectableDateStart) && (dateEnd > owner.SelectableDateEnd);
        }


    }
}
